using System;
using System.Management;
using System.Globalization;

using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace CodePlex.Diagnostics
{
    /// <summary>
    /// Represents a physical processor or processor core.
    /// </summary>
    public sealed class Processor : Device
    {
        #region Processor Private Fields

        private int m_addressWidth;

        private int? m_numberOfCores;

        private ProcessorArchitecture m_architecture;

        private string m_manufacturer;

        #endregion

        #region private Processor()

        /// <summary>
        /// Initializes a new instance of the <see cref="T:Processor"/> class.    
        /// </summary>
        private Processor()
        {

        }

        #endregion

        #region public static IList<Processor> GetProcessors()

        /// <summary>
        /// Gets an <see cref="T:System.Collections.Generic.IList`1"/> containin a <see cref="T:Processor"/> instance for each physical processor or processor core.
        /// </summary>
        /// <remarks>
        /// The <see cref="M:Processor.GetProcessors"/> method is implemented as a method and not a property because the list of processors will take considerably longer
        /// to retrieve than a simple field. Static code analysis warning CA1024 is therefore suppressed after consulting the .NET Framework Design Guidelines written by
        /// Brad Abrams and Krzysztof Cwalina which is published by Addison Wesley.
        /// </remarks>
        /// <returns>An <see cref="T:System.Collections.Generic.IList`1"/> containin a <see cref="T:Processor"/> instance for each physical processor or processor core.</returns>
        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        public static IList<Processor> GetProcessors()
        {
            try
            {
                List<Processor> processors = new List<Processor>();

                ManagementObjectSearcher searcher = new ManagementObjectSearcher(@"root\CIMV2", "SELECT * FROM Win32_Processor");

                foreach (ManagementObject managementObject in searcher.Get())
                {
                    Processor processor = new Processor();

                    processor.AddressWidth = Convert.ToInt32(managementObject["AddressWidth"], CultureInfo.CurrentCulture);

                    int architecture = Convert.ToInt32(managementObject["Architecture"], CultureInfo.CurrentCulture);

                    processor.Architecture = (ProcessorArchitecture)architecture;

                    // Win32_Processor class NumberOfCores property does not exist on versions of the Windows operating system prior to Windows Vista and Windows 2008 (codename Longhorn),
                    // as such this column will be null for exceptions and log entries published on machines running earlier versions of the Windows operating system.                                
                    if (Environment.OSVersion.Version > new Version(6, 0))
                    {
                        processor.NumberOfCores = Convert.ToInt32(managementObject["NumberOfCores"], CultureInfo.CurrentCulture);
                    }
                    else
                    {
                        processor.NumberOfCores = null;
                    }

                    processor.Name = managementObject["Name"] as string;
                    processor.Description = managementObject["Description"] as string;
                    processor.DeviceId = managementObject["DeviceID"] as string;

                    processor.Manufacturer = managementObject["Manufacturer"] as string;

                    processors.Add(processor);
                }

                return new ReadOnlyCollection<Processor>(processors);
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.Message);
                Trace.WriteLine(ex.StackTrace);

                throw;
            }
        } 

        #endregion

        #region public int AddressWidth

        /// <summary>
        /// Gets or sets the processor address width in bits.
        /// </summary>
        /// <value>The processor address width in bits.</value>
        public int AddressWidth
        {
            get
            {
                return m_addressWidth;
            }
            private set
            {
                m_addressWidth = value;
            }
        } 

        #endregion

        #region public ProcessorArchitecture Architecture

        /// <summary>
        /// Gets or sets the processor architecture.
        /// </summary>
        /// <value>The processor architecture.</value>
        public ProcessorArchitecture Architecture
        {
            get
            {
                return m_architecture;
            }
            private set
            {
                m_architecture = value;
            }
        }

        #endregion

        #region public int? NumberOfCores

        /// <summary>
        /// Gets or sets the number of cores within the processor.
        /// </summary>
        /// <remarks>
        /// Only available within the WMI Win32_Processor class for Windows Vista and Windows Server 2008. 
        /// On previous versions of the Windows operating system this value will be <c>null</c>.
        /// </remarks>
        /// <value>The number of cores within the processor.</value>
        public int? NumberOfCores
        {
            get
            {
                return m_numberOfCores;
            }
            private set
            {
                m_numberOfCores = value;
            }
        }

        #endregion

        #region public string Manufacturer

        /// <summary>
        /// Gets or sets the processor manufacturer.
        /// </summary>
        /// <value>The processor manufacturer.</value>
        public string Manufacturer
        {
            get
            {
                return m_manufacturer;
            }
            private set
            {
                m_manufacturer = value;
            }
        }

        #endregion
    }
}
